#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
小米便签网页版导出工具（友好交互版）

本工具是对已有导出工具的优化版本，提供更友好的交互界面和更详细的错误处理。
特点：
- 支持连接到已登录的Chrome浏览器实例
- 提供直观的命令行交互界面
- 详细的错误处理和问题排查建议
- 支持多种导出格式和自定义参数
- 自动检测常见环境问题
"""

import os
import sys
import json
import argparse
import time
import logging
import socket
from datetime import datetime
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException, WebDriverException

# 配置日志
def setup_logger():
    logging.basicConfig(
        level=logging.INFO,
        format='%(asctime)s - %(levelname)s - %(message)s',
        handlers=[
            logging.FileHandler('mi_note_friendly_export.log'),
            logging.StreamHandler()
        ]
    )
    return logging.getLogger(__name__)

logger = setup_logger()

class MiNoteWebExporterFriendly:
    def __init__(self, chrome_driver_path, output_dir="./exports", export_format="json", timeout=30, debug_port=9222):
        """初始化导出工具"""
        self.chrome_driver_path = chrome_driver_path
        self.output_dir = output_dir
        self.export_format = export_format.lower()
        self.timeout = timeout
        self.debug_port = debug_port
        self.driver = None
        
        # 验证输出目录存在
        if not os.path.exists(self.output_dir):
            try:
                os.makedirs(self.output_dir)
                logger.info(f"创建输出目录: {self.output_dir}")
                print(f"✅ 创建输出目录: {self.output_dir}")
            except Exception as e:
                logger.error(f"创建输出目录失败: {str(e)}")
                print(f"❌ 创建输出目录失败: {str(e)}")
                print("   请检查您是否有足够的权限创建目录")
                sys.exit(1)
    
    def check_environment(self):
        """检查环境配置是否正确"""
        print("\n===== 环境检测 =====")
        
        # 检查ChromeDriver文件是否存在
        if not os.path.exists(self.chrome_driver_path):
            logger.error(f"ChromeDriver文件不存在: {self.chrome_driver_path}")
            print(f"❌ ChromeDriver文件不存在: {self.chrome_driver_path}")
            print("   请检查路径是否正确，并确保ChromeDriver已正确下载")
            print("   ChromeDriver下载地址: https://chromedriver.chromium.org/downloads")
            return False
        
        print(f"✅ ChromeDriver文件存在: {self.chrome_driver_path}")
        
        # 检查调试端口是否开放
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(2)
        result = sock.connect_ex(("127.0.0.1", self.debug_port))
        sock.close()
        
        if result != 0:
            logger.error(f"调试端口 {self.debug_port} 未开放")
            print(f"❌ 调试端口 {self.debug_port} 未开放")
            print("   请确认Chrome浏览器是否已使用 --remote-debugging-port=9222 参数启动")
            print("   建议运行提供的start_chrome_debug_mode_simple.bat文件来启动Chrome")
            return False
        
        print(f"✅ 调试端口 {self.debug_port} 已开放")
        print("===== 环境检测通过 =====")
        return True
    
    def connect_to_existing_browser(self):
        """连接到已有的Chrome浏览器实例"""
        print("\n正在连接到已有的Chrome浏览器...")
        
        chrome_options = Options()
        chrome_options.add_experimental_option("debuggerAddress", f"127.0.0.1:{self.debug_port}")
        
        try:
            # 创建WebDriver服务
            service = Service(executable_path=self.chrome_driver_path)
            
            # 连接到已有的Chrome浏览器实例
            self.driver = webdriver.Chrome(service=service, options=chrome_options)
            logger.info("成功连接到已有的Chrome浏览器实例")
            print("✅ 成功连接到Chrome浏览器实例")
            return True
        except WebDriverException as e:
            logger.error(f"连接Chrome浏览器实例失败: {str(e)}")
            print("❌ 连接Chrome浏览器实例失败")
            print(f"   错误详情: {str(e)}")
            print("   请确认：")
            print("   1. 是否已使用 --remote-debugging-port=9222 参数启动Chrome浏览器？")
            print("   2. ChromeDriver版本是否与Chrome浏览器版本匹配？")
            print("   3. 浏览器是否已经打开并可以访问？")
            
            # 提供更多排错建议
            print("\n   排错建议：")
            print("   - 关闭所有Chrome窗口，然后运行start_chrome_debug_mode_simple.bat")
            print("   - 检查Chrome和ChromeDriver版本是否匹配")
            print("   - 确保没有其他程序占用了9222端口")
            return False
        except Exception as e:
            logger.error(f"连接浏览器时发生未知错误: {str(e)}")
            print(f"❌ 连接浏览器时发生未知错误: {str(e)}")
            return False
    
    def check_login_status(self):
        """检查登录状态并导航到便签页面"""
        print("\n检查登录状态并导航到便签页面...")
        try:
            # 导航到小米云服务便签页面
            self.driver.get("https://i.mi.com/note")
            print("✅ 已导航到小米云服务便签页面")
            time.sleep(2)  # 等待页面加载
            
            # 检查是否已登录（通过检查便签列表元素是否存在）
            wait = WebDriverWait(self.driver, self.timeout)
            wait.until(EC.presence_of_element_located((By.CLASS_NAME, "note-list-item")))
            
            logger.info("检测到已登录状态，浏览器已成功访问便签页面")
            print("✅ 检测到已登录状态，找到了便签列表")
            return True
        except TimeoutException:
            logger.error("未检测到登录状态，无法找到便签列表元素")
            print("❌ 未检测到登录状态！")
            print("   请确认：")
            print("   1. 您是否已在浏览器中成功登录小米云服务？")
            print("   2. 浏览器当前是否显示的是便签页面（https://i.mi.com/note）？")
            print("   3. 页面上是否能看到您的便签列表？")
            
            # 提供登录建议
            print("\n   登录建议：")
            print("   - 在Chrome浏览器中，确保您已完成小米账号的登录")
            print("   - 登录后，请手动导航到便签页面并刷新")
            print("   - 确认您能看到自己的便签列表后，再运行此工具")
            return False
        except Exception as e:
            logger.error(f"检查登录状态时发生错误: {str(e)}")
            print(f"❌ 检查登录状态时发生错误: {str(e)}")
            return False
    
    def export_notes(self):
        """导出便签数据"""
        print("\n开始导出便签数据，请稍候...")
        
        try:
            # 等待便签列表加载完成
            wait = WebDriverWait(self.driver, self.timeout)
            wait.until(EC.presence_of_element_located((By.CLASS_NAME, "note-list-item")))
            
            # 获取所有便签元素
            note_elements = self.driver.find_elements(By.CLASS_NAME, "note-list-item")
            logger.info(f"找到 {len(note_elements)} 条便签")
            print(f"✅ 找到 {len(note_elements)} 条便签，正在逐个导出...")
            
            notes_data = []
            success_count = 0
            failed_count = 0
            
            # 遍历每个便签并提取内容
            for i, note_element in enumerate(note_elements, 1):
                try:
                    # 点击便签打开详情
                    note_element.click()
                    time.sleep(1)  # 等待内容加载
                    
                    # 提取标题和内容
                    title = "无标题"
                    content = ""
                    
                    # 尝试获取标题（不同版本的UI可能有所不同）
                    try:
                        title_element = self.driver.find_element(By.CLASS_NAME, "note-editor-title")
                        if title_element.text.strip():
                            title = title_element.text.strip()
                    except:
                        try:
                            # 尝试其他可能的标题元素类名
                            title_element = self.driver.find_element(By.CSS_SELECTOR, ".title-input")
                            if title_element.text.strip():
                                title = title_element.text.strip()
                        except:
                            pass
                    
                    # 尝试获取内容
                    try:
                        content_element = self.driver.find_element(By.CLASS_NAME, "note-editor-content")
                        content = content_element.text.strip()
                    except:
                        try:
                            # 尝试其他可能的内容元素类名
                            content_element = self.driver.find_element(By.CSS_SELECTOR, ".content-input")
                            content = content_element.text.strip()
                        except:
                            pass
                    
                    # 提取便签信息
                    note_data = {
                        "id": i,
                        "title": title,
                        "content": content,
                        "export_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                    }
                    
                    notes_data.append(note_data)
                    success_count += 1
                    logger.info(f"已提取便签 {i}/{len(note_elements)}: {title}")
                    
                    # 显示进度
                    if i % 5 == 0 or i == len(note_elements):
                        progress = (i / len(note_elements)) * 100
                        print(f"   进度: {i}/{len(note_elements)} ({progress:.1f}%) - 已提取 '{title}'")
                    
                except Exception as e:
                    failed_count += 1
                    logger.error(f"提取便签 {i} 时发生错误: {str(e)}")
                    print(f"⚠️ 提取便签 {i} 时发生错误，跳过此条")
                    continue
            
            print(f"\n✅ 便签提取完成 - 成功: {success_count}, 失败: {failed_count}")
            
            # 保存导出的数据
            self._save_exported_data(notes_data)
            return True
        except Exception as e:
            logger.error(f"导出便签过程中发生错误: {str(e)}")
            print(f"❌ 导出便签过程中发生错误: {str(e)}")
            return False
    
    def _save_exported_data(self, notes_data):
        """保存导出的数据到文件"""
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        
        if self.export_format == "json":
            # 保存为JSON格式
            file_path = os.path.join(self.output_dir, f"mi_notes_{timestamp}.json")
            try:
                with open(file_path, "w", encoding="utf-8") as f:
                    json.dump(notes_data, f, ensure_ascii=False, indent=2)
                logger.info(f"已将 {len(notes_data)} 条便签导出为JSON格式: {file_path}")
                print(f"\n✅ 已将 {len(notes_data)} 条便签导出为JSON格式:")
                print(f"   {file_path}")
            except Exception as e:
                logger.error(f"保存JSON文件失败: {str(e)}")
                print(f"❌ 保存JSON文件失败: {str(e)}")
                print("   请检查您是否有写入权限")
            
        elif self.export_format == "txt":
            # 保存为TXT格式（每个便签一个文件）
            txt_dir = os.path.join(self.output_dir, f"mi_notes_txt_{timestamp}")
            try:
                if not os.path.exists(txt_dir):
                    os.makedirs(txt_dir)
                
                for note in notes_data:
                    # 生成安全的文件名
                    safe_title = "".join([c for c in note["title"] if c.isalnum() or c in " -_"]).rstrip()
                    if not safe_title:
                        safe_title = f"无标题_{note['id']}"
                    
                    file_path = os.path.join(txt_dir, f"{safe_title}.txt")
                    
                    # 避免文件名冲突
                    counter = 1
                    base_path = file_path
                    while os.path.exists(file_path):
                        name, ext = os.path.splitext(base_path)
                        file_path = f"{name}_{counter}{ext}"
                        counter += 1
                    
                    # 写入文件
                    with open(file_path, "w", encoding="utf-8") as f:
                        if note["title"] != "无标题":
                            f.write(f"{note['title']}\n\n")
                        f.write(note["content"])
                        f.write(f"\n\n导出时间: {note['export_time']}")
                
                logger.info(f"已将 {len(notes_data)} 条便签导出为TXT格式: {txt_dir}")
                print(f"\n✅ 已将 {len(notes_data)} 条便签导出为TXT格式（每个便签一个文件）:")
                print(f"   {txt_dir}")
            except Exception as e:
                logger.error(f"保存TXT文件失败: {str(e)}")
                print(f"❌ 保存TXT文件失败: {str(e)}")
                print("   请检查您是否有写入权限")
            
    def close(self):
        """关闭连接（不会关闭用户的浏览器）"""
        if self.driver:
            self.driver.quit()
            logger.info("已断开与浏览器的连接")
            print("\n✅ 已断开与浏览器的连接")

def parse_args():
    """解析命令行参数"""
    parser = argparse.ArgumentParser(description='小米便签网页版导出工具（友好交互版）')
    parser.add_argument('--driver_path', required=True, help='ChromeDriver的路径')
    parser.add_argument('--output_dir', default='./exports', help='导出文件的保存目录')
    parser.add_argument('--format', default='json', choices=['json', 'txt'], help='导出格式')
    parser.add_argument('--timeout', type=int, default=30, help='操作超时时间（秒）')
    parser.add_argument('--debug_port', type=int, default=9222, help='Chrome调试端口')
    return parser.parse_args()

def print_welcome_message():
    """打印欢迎信息"""
    print("="*60)
    print("        小米便签网页版导出工具（友好交互版）        ")
    print("="*60)
    print("\n使用说明：")
    print("1. 请确保已使用以下命令行参数启动Chrome浏览器：")
    print("   chrome.exe --remote-debugging-port=9222")
    print("   （或运行提供的start_chrome_debug_mode_simple.bat文件）")
    print("2. 在打开的浏览器中登录您的小米云服务账号，并导航到便签页面")
    print("3. 确保您能看到自己的便签列表")
    print("4. 运行本工具，它将自动连接到已登录的浏览器并导出便签")
    print("\n注意：\n- 请保持Chrome浏览器处于打开状态，直到导出完成\n- 导出的文件将保存在指定的输出目录中")
    print("="*60)
    
    # 等待用户确认
    input("\n已确认上述步骤，请按Enter键继续...")

def main():
    # 打印欢迎信息
    print_welcome_message()
    
    # 解析命令行参数
    args = parse_args()
    
    logger.info(f"程序启动 - 导出格式: {args.format}, 输出目录: {args.output_dir}, 超时时间: {args.timeout}秒, ChromeDriver路径: {args.driver_path}")
    
    try:
        # 创建导出工具实例
        exporter = MiNoteWebExporterFriendly(
            chrome_driver_path=args.driver_path,
            output_dir=args.output_dir,
            export_format=args.format,
            timeout=args.timeout,
            debug_port=args.debug_port
        )
        
        # 检查环境配置
        if not exporter.check_environment():
            print("\n❌ 环境检测失败，程序退出")
            return 1
        
        # 连接到已有的Chrome浏览器实例
        if not exporter.connect_to_existing_browser():
            print("\n❌ 连接浏览器失败，程序退出")
            return 1
        
        # 检查登录状态
        if not exporter.check_login_status():
            print("\n❌ 未检测到登录状态，程序退出")
            exporter.close()
            return 1
        
        # 导出便签
        if not exporter.export_notes():
            print("\n❌ 导出便签失败，程序退出")
            exporter.close()
            return 1
        
        # 完成导出
        exporter.close()
        logger.info("导出完成！")
        print("\n" + "="*60)
        print("✅ 导出成功！")
        print(f"导出文件保存在: {args.output_dir}")
        print("感谢使用小米便签网页版导出工具！")
        print("="*60)
        return 0
        
    except KeyboardInterrupt:
        logger.info("用户中断程序")
        print("\n⚠️ 程序被用户中断")
        return 1
    except Exception as e:
        logger.error(f"程序运行失败: {str(e)}")
        print(f"\n❌ 程序运行失败: {str(e)}")
        print("请查看日志文件mi_note_friendly_export.log获取更多详情")
        return 1

if __name__ == "__main__":
    exit_code = main()
    os._exit(exit_code)